// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: wsfuyibing <682805@qq.com>
// Date: 2024-12-11

package filter

import (
	"context"
	"fmt"
	"gitee.com/go-libs/log"
	"gitee.com/gomq/sr/errs"
	"strings"
)

type (
	// Field is a component used to store filter condition unit.
	Field interface {
		// GetError return the first error in the error list.
		GetError() error

		// GetKey return the key of the field.
		GetKey() Key

		// GetKind return the kind of the field.
		GetKind() KeyKind

		// GetOperator return the operator of the field.
		GetOperator() Operator

		// GetValue returns the expect value of the field.
		GetValue() Value

		// HasError return true if error occurred when parse done.
		HasError() bool

		// Match matches the given value with expect value(s), Return true if
		// match success otherwise false returned.
		Match(ctx context.Context, data map[string]any) bool

		// Query returns a filter query expression like sql with field name.
		//
		//   - status == 1
		//   - catalog IN ('processing')
		Query() string
	}

	field struct {
		Err       error
		ErrExists bool
		Kind      KeyKind
		Key       Key
		Operator  Operator
		Value     Value

		query string
	}
)

// NewField creates a field component.
//
//	filter.NewField(`status == 1`)
//	filter.NewField(`status == 1 OR status == 2`)
func NewField(str string) Field {
	o := &field{}
	o.Err = o.parse(str)

	o.ready()
	o.buildQuery()
	return o
}

// + Implemented

func (o *field) GetError() error                                     { return o.Err }
func (o *field) GetKey() Key                                         { return o.Key }
func (o *field) GetKind() KeyKind                                    { return o.Kind }
func (o *field) GetOperator() Operator                               { return o.Operator }
func (o *field) GetValue() Value                                     { return o.Value }
func (o *field) HasError() bool                                      { return o.ErrExists }
func (o *field) Match(ctx context.Context, data map[string]any) bool { return o.match(ctx, data) }
func (o *field) Query() string                                       { return o.query }

// + Match

func (o *field) match(ctx context.Context, data map[string]any) bool {
	// Seek value from data then match it.
	if v, has := o.Key.Seek(data); has {
		return o.Value.Match(ctx, v)
	}

	// Return false if field not defined in data.
	if log.Config().DebugOn() {
		log.Debugfc(ctx, `[filter][field=%s] skipped: field not defined in data`, o.Key)
	}
	return false
}

// + Access

func (o *field) buildQuery() {
	// Return if error exists.
	if o.ErrExists {
		return
	}

	// Generate for kind is count.
	if o.Kind.IsCount() {
		o.query = fmt.Sprintf("COUNT(%s) %s", o.Key, o.Value.Query())
		return
	}

	// Generate for other kind.
	o.query = fmt.Sprintf("%s %s", o.Key, o.Value.Query())
}

func (o *field) parse(str string) error {
	var m []string

	// Return error if expression is an empty string.
	if str = strings.TrimSpace(str); str == "" {
		return errs.ErrFilterFieldExpressionNotSpecified
	}

	// Remove brackets with head and end pairs of the expression.
	if regexMatchBracket.MatchString(str) {
		str = regexMatchBracket.ReplaceAllString(str, "$1")
		return o.parse(str)
	}

	// Field expression format must be `Key Operator Expect` like `key == 1`,
	// `key in (1, 2, 3)` and so on.
	//
	//   m := {
	//     0: ".status == 1",
	//     1: ".",
	//     2: "status",
	//     3: "==",
	//     4: "1",
	//   }
	if m = regexMatchField.FindStringSubmatch(str); len(m) == 0 {
		return errs.ErrFilterFieldExpressionNotValid
	}

	// Set key, kind and operator for the field.
	o.Kind = KeyKind(m[1]).Defaulter()
	o.Key = Key(m[2])
	o.Operator = Operator(strings.ToUpper(m[3])).Defaulter()

	// Build respect value based on operator type.
	switch o.Operator {
	case OperatorGreater, OperatorGreaterEqual, OperatorLess, OperatorLessEqual:
		o.Value = (&valueForRange{Field: o}).init(m[4])
	case OperatorIn, OperatorNotIn:
		o.Value = (&valueForIn{Field: o}).init(m[4])
	case OperatorEqual, OperatorNotEqual:
		o.Value = (&valueForEqual{Field: o}).init(m[4])
	default:
		return errs.ErrFilterUnsupportedOperator
	}
	return nil
}

func (o *field) ready() {
	// Check if value is nil or has error.
	if o.Value == nil {
		o.Err = errs.ErrFilterBuildExpectValueFailed
	} else if o.Value.HasError() {
		o.Err = o.Value.GetError()
	}

	// Check if error exists.
	o.ErrExists = o.Err != nil
}
